#include "win_nt.h"
#include <SDKDDKVer.h>
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>
#include <winuser.h>
// dui头文件
#include "msg_core.h"
#include <stdio.h>
#include "UIContainer.h"

const wchar_t *SHELL_WIN_CLASS = L"duilib_pro_main_class";

void translate_pt_scr2client(DuiWinManager *mgr, int *x, int *y)
{
    if (mgr != nullptr && mgr->hWin != nullptr)
    {
        POINT point = {*x, *y};
        ScreenToClient((HWND)mgr->hWin, &point);
        *x = point.x;
        *y = point.y;
    }
}
bool translate_message(DuiWinManager *mgr, DUI_WIN_MSG msg, UINT nativeMsg, size_t wParam, size_t lParam)
{
    if (mgr != nullptr && mgr->default_msg_dispense != nullptr)
    {
        void *pEvent = nullptr;
        if (msg == DUI_WIN_MSG_CREATE)
        {
            RECT rect;
            GetWindowRect((HWND)mgr->hWin, &rect);
            mgr->rect = DuiRect{rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top};
            shell_win_set_cursor(DUI_CURSOR_STYLE_ARROW);
        }
        if (msg == DUI_WIN_MSG_MOUSE_MOVE || msg == DUI_WIN_MSG_MOUSE_KDOWN || msg == DUI_WIN_MSG_MOUSE_KUP ||
            msg == DUI_WIN_MSG_MOUSE_DBCLICK || msg == DUI_WIN_MSG_MOUSE_WHEEL)
        {
            // event
            auto mouseEvent = DuiEventMouse();
            mouseEvent.x = LOWORD(lParam);
            mouseEvent.y = HIWORD(lParam);
            mouseEvent.button = DUI_MOUSE_BTN_NONE;
            mouseEvent.wheel = 0;
            mouseEvent.action = 0;
            if (nativeMsg == WM_LBUTTONDOWN || nativeMsg == WM_RBUTTONDOWN || nativeMsg == WM_MBUTTONDOWN)
            {
                mouseEvent.action = 1;
                mouseEvent.button = LOWORD(wParam);
            }
            else if (nativeMsg == WM_LBUTTONUP || nativeMsg == WM_RBUTTONUP || nativeMsg == WM_MBUTTONUP)
            {
                mouseEvent.action = 2;
                mouseEvent.button = LOWORD(wParam);
            }
            else if (nativeMsg == WM_LBUTTONDBLCLK || nativeMsg == WM_RBUTTONDBLCLK || nativeMsg == WM_MBUTTONDBLCLK)
            {
                mouseEvent.action = 3;
                mouseEvent.button = LOWORD(wParam);
            }
            else if (nativeMsg == WM_MOUSEWHEEL)
            {
                // x,y 坐标修正，wheel是相对屏幕左上角
                mouseEvent.x -= mgr->rect.x;
                mouseEvent.y -= mgr->rect.y;
                mouseEvent.action = 4;
                mouseEvent.wheel = GET_WHEEL_DELTA_WPARAM(wParam);
            }
            pEvent = &mouseEvent;

            PostMessage((HWND)mgr->hWin, WM_PAINT, 0, 0);
        }
        if (msg == DUI_WIN_MSG_KBD_DOWN || msg == DUI_WIN_MSG_KBD_UP || msg == DUI_WIN_MSG_KBD_CHAR)
        {
            // event
            auto keyEvent = DuiEventKbd();
            keyEvent.key = (wParam);
            keyEvent.action = 0;
            if (nativeMsg == WM_KEYDOWN || nativeMsg == WM_SYSKEYDOWN)
            {
                keyEvent.action = 1;
            }
            else if (nativeMsg == WM_KEYUP || nativeMsg == WM_SYSKEYUP)
            {
                keyEvent.action = 2;
            }
            else if (nativeMsg == WM_CHAR || nativeMsg == WM_SYSCHAR)
            {
                keyEvent.action = 3;
            }
            // key state
            keyEvent.sys = 0;
            if (GetKeyState(VK_CONTROL) & 0x8000)
            {
                keyEvent.sys |= DUI_KBD_SYS_CTRL;
            }
            if (GetKeyState(VK_SHIFT) & 0x8000)
            {
                keyEvent.sys |= DUI_KBD_SYS_SHIFT;
            }
            if (GetKeyState(VK_MENU) & 0x8000)
            {
                keyEvent.sys |= DUI_KBD_SYS_ALT;
            }
            if (GetKeyState(VK_LWIN) & 0x8000)
            {
                keyEvent.sys |= DUI_KBD_SYS_WIN;
            }
            if (GetKeyState(VK_RWIN) & 0x8000)
            {
                keyEvent.sys |= DUI_KBD_SYS_WIN;
            }
            if (GetKeyState(VK_SNAPSHOT) & 0x8000)
            {
                keyEvent.sys |= DUI_KBD_SYS_SNAP;
            }

            pEvent = &keyEvent;

            PostMessage((HWND)mgr->hWin, WM_PAINT, 0, 0);
        }
        if (msg == DUI_WIN_MSG_COMMAND)
        {
            auto cmdEvent = DuiEventCommand();
            cmdEvent.fromHwnd = (HWND)lParam;
            cmdEvent.cmd = LOWORD(wParam);
            pEvent = &cmdEvent;
        }
        if (msg == DUI_WIN_MSG_CONTEXTMENU)
        {
            auto menuEvent = DuiEventContextMenu();
            menuEvent.x = LOWORD(lParam);
            menuEvent.y = HIWORD(lParam);
            translate_pt_scr2client(mgr, &menuEvent.x, &menuEvent.y);
            menuEvent.button = LOWORD(wParam);
            pEvent = &menuEvent;
        }
        if (msg == DUI_WIN_MSG_MOVE)
        {
            auto moveEvent = DuiEventMove();
            moveEvent.x = LOWORD(lParam);
            moveEvent.y = HIWORD(lParam);
            mgr->rect.x = moveEvent.x;
            mgr->rect.y = moveEvent.y;
            pEvent = &moveEvent;
        }
        if (msg == DUI_WIN_MSG_RESIZE)
        {
            auto sizeEvent = DuiEventResize();
            sizeEvent.w = LOWORD(lParam);
            sizeEvent.h = HIWORD(lParam);
            mgr->rect.w = sizeEvent.w;
            mgr->rect.h = sizeEvent.h;
            pEvent = &sizeEvent;
        }
        if (msg == DUI_WIN_MSG_ACTIVATE)
        {
            auto activeEvent = DuiEventActive();
            activeEvent.active = LOWORD(wParam);
            activeEvent.isMinimized = HIWORD(wParam) == 0 ? false : true;
            mgr->active = activeEvent.active;
            mgr->isMinimized = activeEvent.isMinimized;
            pEvent = &activeEvent;
        }
        if (msg == DUI_WIN_MSG_SHOW)
        {
            auto showEvent = DuiEventShow();
            showEvent.show = (wParam);
            mgr->isShow = showEvent.show;
            pEvent = &showEvent;
        }
        if (msg == DUI_WIN_MSG_COPYDATA)
        {
            HWND hWndSender = (HWND)wParam;
            COPYDATASTRUCT *pcds = (COPYDATASTRUCT *)lParam;
            auto copyData = DuiEventCopyData();
            copyData.fromHwnd = hWndSender;
            copyData.data = pcds->lpData;
            copyData.len = pcds->cbData;
            copyData.type = pcds->dwData;

            pEvent = &copyData;
        }
        if (msg == DUI_WIN_MSG_PAINT)
        {
            pEvent = &mgr->rect;

            // 默认绘制
            if (mgr->default_paint_dispense != nullptr)
            {
                mgr->default_paint_dispense(mgr);
            }
        }
        if (msg == DUI_WIN_MSG_SETCURSOR)
        {
            // 拦截自绘UI的cursor设定覆盖
            if (mgr->pCurHoverUI != nullptr && mgr->pCurHoverUI != mgr->pRoot)
            {
                return false;
            }
        }
        if (msg == DUI_WIN_MSG_IME_CHAR)
        {
            // https://www.cnblogs.com/linuxheik/articles/11411124.html

            auto keyEvent = DuiEventKbd();
            keyEvent.key = (wParam);
            keyEvent.action = 3;
            pEvent = &keyEvent;
        }

        return mgr->default_msg_dispense(mgr, msg, pEvent, nativeMsg);
    }
    return true;
}

LRESULT CALLBACK _WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // default process
    bool is_nt_proc = true;
    // msg manager
    DuiWinManager *mgr = nullptr;
    shell_mgr_find(hWnd, &mgr);

    // msg process
    switch (message)
    {
    case WM_CREATE: {
        // 在 CreateWindow时候产生，所以mgr无法响应
        // translate_message(mgr, DUI_WIN_MSG_CREATE, message, wParam, lParam);
        SetFocus(hWnd);
        SetTimer(hWnd, 1, 500, nullptr);
    }
    break;
    case WM_SIZE: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_RESIZE, message, wParam, lParam);
    }
    break;
    case WM_MOVE: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_MOVE, message, wParam, lParam);
    }
    break;
    case WM_ACTIVATE: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_ACTIVATE, message, wParam, lParam);
    }
    break;

    case WM_SHOWWINDOW: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_SHOW, message, wParam, lParam);
    }
    break;

    // 键盘消息
    case WM_COMMAND: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_COMMAND, message, wParam, lParam);
    }
    break;
    case WM_SYSCOMMAND: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_COMMAND, message, wParam, lParam);
    }
    break;
    case WM_KEYDOWN: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_KBD_DOWN, message, wParam, lParam);
    }
    break;
    case WM_KEYUP: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_KBD_UP, message, wParam, lParam);
    }
    break;
    case WM_CHAR: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_KBD_CHAR, message, wParam, lParam);
    }
    break;
    case WM_SYSKEYDOWN: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_KBD_DOWN, message, wParam, lParam);
    }
    break;
    case WM_SYSKEYUP: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_KBD_UP, message, wParam, lParam);
    }
    break;
    case WM_SYSCHAR: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_KBD_CHAR, message, wParam, lParam);
    }
    break;

    // 输入法相关
    case WM_IME_STARTCOMPOSITION: {
        if(mgr->pCurFocusUI == nullptr || mgr->pCurFocusUI == mgr->pRoot){
            break;
        }
        // 控制 ime cadia 显示位置
        HIMC hIMC = ImmGetContext(hWnd);
        if (hIMC)
        {
            POINT point;
            GetCaretPos(&point);
            printf("GetCaretPos: %d, %d\n", point.x, point.y);
            COMPOSITIONFORM Composition;
            Composition.dwStyle = CFS_POINT;
            Composition.ptCurrentPos.x = point.x;
            Composition.ptCurrentPos.y = point.y;
            ImmSetCompositionWindow(hIMC, &Composition);
            // font
            if(mgr->pCurFocusUI != nullptr ){
                auto pFocus = (UIContainer*)mgr->pCurFocusUI;
                // 设置输入法字体
                //ImmSetCompositionFontW(hIMC, nullptr);
            }
            ImmReleaseContext(hWnd, hIMC);
        }
    }
    break;
    case WM_IME_CHAR: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_IME_CHAR, message, wParam, lParam);
        is_nt_proc = false;
    }
    break;

    // 鼠标消息
    case WM_MOUSEMOVE: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_MOUSE_MOVE, message, wParam, lParam);
    }
    break;
    case WM_LBUTTONDOWN:
    case WM_RBUTTONDOWN:
    case WM_MBUTTONDOWN: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_MOUSE_KDOWN, message, wParam, lParam);
    }
    break;
    case WM_LBUTTONUP:
    case WM_RBUTTONUP:
    case WM_MBUTTONUP: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_MOUSE_KUP, message, wParam, lParam);
    }
    break;
    // 需要添加 DBCLICK flags 才可以响应
    case WM_LBUTTONDBLCLK:
    case WM_RBUTTONDBLCLK:
    case WM_MBUTTONDBLCLK: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_MOUSE_DBCLICK, message, wParam, lParam);
    }
    break;
    case WM_MOUSEWHEEL: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_MOUSE_WHEEL, message, wParam, lParam);
    }
    break;
    case WM_DROPFILES: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_DROPS, message, wParam, lParam);
    }
    break;
    case WM_COPYDATA: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_COPYDATA, message, wParam, lParam);
    }
    break;
    case WM_CONTEXTMENU: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_CONTEXTMENU, message, wParam, lParam);
    }
    break;

    case WM_PAINT: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_PAINT, message, wParam, lParam);
    }
    break;
    case WM_SETCURSOR: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_SETCURSOR, message, wParam, lParam);
    }
    break;
    case WM_CLOSE: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_CLOSE, message, wParam, lParam);
        // 用户阻止销毁窗口
        if (is_nt_proc)
        {
            DestroyWindow(hWnd);
        }
    }
    break;
    case WM_DESTROY:
        translate_message(mgr, DUI_WIN_MSG_DESTROY, message, wParam, lParam);
        if (mgr != nullptr)
        {
            shell_mgr_unregister(mgr);
        }
        KillTimer(hWnd, 1);
        PostQuitMessage(0);
        break;
    case WM_TIMER: {
        is_nt_proc = translate_message(mgr, DUI_WIN_MSG_TIMER, message, wParam, lParam);
    }
    default:
        break;
    }

    // 调用默认的窗口过程
    if (is_nt_proc)
    {
        return DefWindowProcW(hWnd, message, wParam, lParam);
    }
    return 0;
}

void _register_class(HINSTANCE hInstance, int flags)
{
    WNDCLASSEXW wc;
    wc.cbSize = sizeof(WNDCLASSEXW);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = _WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = nullptr;
    wc.lpszClassName = SHELL_WIN_CLASS;
    wc.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);

    if (flags & DUI_WIN_FLAG_DBCLICK)
    {
        wc.style |= CS_DBLCLKS;
    }

    RegisterClassExW(&wc);
}

bool shell_init(DuiWinManager *mgr, int w, int h, const char *title, int flags)
{
    auto handle = GetModuleHandle(nullptr);
    // register winproc
    _register_class(handle, flags);
    // create main window
    wchar_t buf[1024];
    utf8_to_utf16(title, buf, 1024);

    // flags
    int win_flags = WS_OVERLAPPEDWINDOW;
    if (flags & DUI_WIN_FLAG_FRAMELESS)
    {
        win_flags = WS_POPUP;
    }
    if (flags & DUI_WIN_FLAG_FIXED)
    {
        win_flags &= ~(WS_MAXIMIZEBOX | WS_THICKFRAME);
    }
    if (flags & DUI_WIN_FLAG_CLOSEABLE)
    {
        win_flags &= ~(WS_SYSMENU & 0xF0000);
    }
    auto hWnd =
        CreateWindowExW(0, SHELL_WIN_CLASS, buf, win_flags, CW_USEDEFAULT, 0, w, h, nullptr, nullptr, handle, nullptr);
    if (!hWnd)
        return false;

    // set msg manager
    mgr->hWin = hWnd;
    mgr->hInst = handle;
    shell_mgr_register(mgr);
    translate_message(mgr, DUI_WIN_MSG_CREATE, WM_CREATE, 0, 0);

    // show window
    ShowWindow(hWnd, SW_SHOWDEFAULT);
    UpdateWindow(hWnd);
    return true;
}

void shell_loop(DuiWinManager *mgr)
{
    PostMessageW((HWND)mgr->hWin, WM_PAINT, 0, 0);

    MSG msg;
    // 主消息循环:
    while (GetMessageW(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
}

void shell_quit(DuiWinManager *mgr)
{
    PostQuitMessage(0);
}

void shell_win_move(DuiWinManager *mgr, int x, int y)
{
    SetWindowPos((HWND)mgr->hWin, nullptr, x, y, 0, 0, SWP_NOSIZE);
}

void shell_win_size(DuiWinManager *mgr, int w, int h)
{
    SetWindowPos((HWND)mgr->hWin, nullptr, 0, 0, w, h, SWP_NOMOVE);
}

void shell_win_title(DuiWinManager *mgr, const char *title)
{
    wchar_t buf[1024];
    utf8_to_utf16(title, buf, 1024);
    SetWindowTextW((HWND)mgr->hWin, buf);
}

void shell_win_show(DuiWinManager *mgr, bool show, bool isMinimize)
{
    auto flags = SW_HIDE;
    if (show)
    {
        flags = SW_SHOWDEFAULT;
        if (isMinimize)
        {
            flags = SW_SHOWMINIMIZED;
        }
    }
    ShowWindow((HWND)mgr->hWin, flags);
}

void shell_win_center(DuiWinManager *mgr)
{
    auto hWnd = (HWND)mgr->hWin;

    // 获得屏幕尺寸
    auto screenWidth = GetSystemMetrics(SM_CXSCREEN);
    auto screenHeight = GetSystemMetrics(SM_CYSCREEN);

    // 获得窗口尺寸
    RECT rect;
    GetWindowRect(hWnd, &rect);
    auto winWidth = rect.right - rect.left;
    auto winHeight = rect.bottom - rect.top;

    // 计算窗口位置
    auto x = (screenWidth - winWidth) / 2;
    auto y = (screenHeight - winHeight) / 2;

    // 移动窗口到指定位置
    shell_win_move(mgr, x, y);
}

void shell_win_set_icon(DuiWinManager *mgr, const char *icon)
{
    auto ic = LoadImageA(nullptr, icon, IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
    SendMessage((HWND)mgr->hWin, WM_SETICON, FALSE, (LPARAM)ic);
}

void shell_win_set_cursor(DUI_CURSOR_STYLE cursor)
{
    HCURSOR hCursor = nullptr;
    switch (cursor)
    {
    case DUI_CURSOR_STYLE_ARROW:
        hCursor = LoadCursor(nullptr, IDC_ARROW);
        break;
    case DUI_CURSOR_STYLE_IBEAM:
        hCursor = LoadCursor(nullptr, IDC_IBEAM);
        break;
    case DUI_CURSOR_STYLE_WAIT:
        hCursor = LoadCursor(nullptr, IDC_WAIT);
        break;
    case DUI_CURSOR_STYLE_CROSS:
        hCursor = LoadCursor(nullptr, IDC_CROSS);
        break;
    case DUI_CURSOR_STYLE_UPARROW:
        hCursor = LoadCursor(nullptr, IDC_UPARROW);
        break;
    case DUI_CURSOR_STYLE_SIZENWSE:
        hCursor = LoadCursor(nullptr, IDC_SIZENWSE);
        break;
    case DUI_CURSOR_STYLE_SIZENESW:
        hCursor = LoadCursor(nullptr, IDC_SIZENESW);
        break;
    case DUI_CURSOR_STYLE_SIZEWE:
        hCursor = LoadCursor(nullptr, IDC_SIZEWE);
        break;
    case DUI_CURSOR_STYLE_SIZENS:
        hCursor = LoadCursor(nullptr, IDC_SIZENS);
        break;
    case DUI_CURSOR_STYLE_SIZEALL:
        hCursor = LoadCursor(nullptr, IDC_SIZEALL);
        break;
    case DUI_CURSOR_STYLE_NO:
        hCursor = LoadCursor(nullptr, IDC_NO);
        break;
    case DUI_CURSOR_STYLE_HAND:
        hCursor = LoadCursor(nullptr, IDC_HAND);
        break;
    case DUI_CURSOR_STYLE_APPSTARTING:
        hCursor = LoadCursor(nullptr, IDC_APPSTARTING);
        break;
    case DUI_CURSOR_STYLE_HELP:
        hCursor = LoadCursor(nullptr, IDC_HELP);
        break;
    default:
        break;
    }

    if (hCursor)
    {
        SetCursor(hCursor);
    }
}

void shell_win_set_transparent(DuiWinManager *mgr, DuiColor color, bool useAlpha)
{
    // 分层窗口会失去 WM_PAINT message
    auto style = GetWindowLong((HWND)mgr->hWin, GWL_EXSTYLE);
    SetWindowLong((HWND)mgr->hWin, GWL_EXSTYLE, style | WS_EX_LAYERED);

    // 设置透明色和透明度
    DWORD flags = LWA_COLORKEY;
    if (useAlpha)
        flags |= LWA_ALPHA;
    COLORREF colorKey = RGB(color.r, color.g, color.b);
    SetLayeredWindowAttributes((HWND)mgr->hWin, colorKey, color.a, flags);
}

int utf16_to_utf8(const wchar_t *utf16, char *utf8, int len)
{
    return WideCharToMultiByte(CP_UTF8, 0, utf16, -1, utf8, len, nullptr, nullptr);
    return 0;
}

int utf8_to_utf16(const char *utf8, wchar_t *utf16, int len)
{
    return MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, len);
}
